#!/usr/bin/python
#
# Copyright 2009, Josef Kyrian <josef.kyrian@gmail.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

import sys
from PyQt4 import QtGui, QtCore, Qt
import time
import g
import menu
from config import config
import ir
import control
# import module to simulate key events
import KeySimulator
from dbglog import dbg


class HTPCControllerEventListener(QtGui.QLabel):
	"""
	HTPCControllerMenu
	"""
	
	def __init__(self, htpcController, allowStandartInput):
		"""
		Init
		"""
		self.htpcController = htpcController
		
		QtGui.QLabel.__init__(self, None, {True: QtCore.Qt.FramelessWindowHint, False: QtCore.Qt.ToolTip}[allowStandartInput])

		self.setWindowTitle('HTPCControllerListener')
		
		if (allowStandartInput):
			self.setGeometry(0, 0, 1, 1)
		else:
			self.setGeometry(0, 0, 0, 0)
		#endif
	#enddef
	
	
	def keyPressEvent(self, event):
		"""
		KeyPress event
		"""
		self.htpcController.keyPressEvent(event)
	#enddef
#endclass


class HTPCControllerMenu(QtGui.QLabel):
	"""
	HTPCControllerMenu
	"""
	
	# htpcController
	htpcController = None
	
	# menuItems
	menuItems = None
	
	# menu window
	gMenu = None
	
	# x coordinate
	x = 100
	
	# y coordinate
	y = 100
	
	# width
	width = 100
	
	# height
	height = 100
	
	# selected item number
	selectedItem = 0
	
	# height of item
	itemHeight = 0
	
	
	def __init__(self, htpcController, menuRaw, x, y):
		"""
		Init
		"""
		self.htpcController = htpcController
		self.menuItems = menu.getSubmenuItems(menuRaw)
		self.x = x
		self.y = y
		
		QtGui.QLabel.__init__(self, None, QtCore.Qt.ToolTip)
		self.setWindowOpacity(0.9)

		self.htpcController.currentMenuStack.append(self)
		
		self.setWindowTitle('HTPCController')
		
		fm = QtGui.QFontMetrics(config.itemFont)
		self.itemHeight = fm.height() * 1.2
		
		self.selectedItem = 0
		
		# try to compute window size
		width = 100
		height = 0
		paddingWidth = 20 + fm.width('WW')
		
		fm = QtGui.QFontMetrics(config.itemFont)
		for item in self.menuItems:
			height += self.itemHeight
			rowWidth = fm.width(item['title']) + paddingWidth
			if (rowWidth > width):
				width = rowWidth
			#endif
		#endfor
		self.width = width
		self.height = height
		
		self.setGeometry(self.x, self.y, self.width, self.height)
	#enddef
	
	
	def _drawOutlineText(self, paint, penTextOutline, penTextFill, rc, options, text):
		"""
		Draws outlined text
		"""
		# outline text
		rc.moveTopLeft(Qt.QPoint(rc.left() + 1, rc.top() + 1))
		paint.setPen(penTextOutline)
		paint.drawText(rc, options, text)
		
		# fill text
		rc.moveTopLeft(Qt.QPoint(rc.left() - 1, rc.top() - 1))
		paint.setPen(penTextFill)
		paint.drawText(rc, options, text)
	#enddef
	
	
	def getSelectedItem(self):
		"""
		Returns selected item
		"""
		return self.menuItems[self.selectedItem]
	#enddef
	
	
	def selectNextItem(self):
		"""
		Selects next item
		"""
		if (self.selectedItem + 1 < len(self.menuItems)):
			self.selectedItem += 1
		else:
			self.selectedItem = 0
		#endif
		self.repaint()
	#enddef
	
	
	def selectPreviousItem(self):
		"""
		Selects next item
		"""
		if (self.selectedItem > 0):
			self.selectedItem -= 1
		else:
			self.selectedItem = len(self.menuItems) - 1
		#endif
		self.repaint()
	#enddef
	
	
	def clickSelectedItem(self):
		"""
		Clicks selected item
		"""
		selectedItem = self.getSelectedItem()
		if (selectedItem.get('items', [])):
			self.gMenu = HTPCControllerMenu(self.htpcController, selectedItem, self.x + self.width - 20, self.y + 20 + self.selectedItem * self.itemHeight)
			self.gMenu.show()
		else:
			if (selectedItem['command']):
				try:
					selectedItem['command']()
				except Exception, e:
					dbg.log('Menu command exception: %s', str(e))
					print e
				#endtry
			#endif
		#endif
	#enddef


	def paintEvent(self, event):
		"""
		Repaint event
		"""
		
		paint = QtGui.QPainter()
		paint.begin(self)
		paint.fillRect(Qt.QRect(0, 0, self.width, self.height), QtGui.QBrush(config.bgColor))
		penBackgroundOutline = QtGui.QPen(config.bgOutlineColor)
		paint.drawRect(Qt.QRect(0, 0, self.width - 1, self.height - 1))
		paint.setFont(config.itemFont)
		penTextOutline = QtGui.QPen(config.itemOutlineColor)
		penTextFill = QtGui.QPen(config.itemColor)
		penSelectedItemBackgroundOutline = QtGui.QPen(config.selectedItemBgOutlineColor)
		
		x = 10
		y = 0
		for i, item in enumerate(self.menuItems):
			rcItem = Qt.QRect(0, y, self.width - 1, self.itemHeight - 1)
			
			# background
			if (i == self.selectedItem):
				paint.fillRect(rcItem, QtGui.QBrush(config.selectedItemBgColor))
				paint.setPen(penSelectedItemBackgroundOutline)
				paint.drawRect(rcItem)
			#endif
			
			rc = Qt.QRect(x, y, self.width - 20, self.itemHeight)
			self._drawOutlineText(paint, penTextOutline, penTextFill, rc, QtCore.Qt.AlignLeft, item['title'])
			
			if (item.get('items', [])):
				self._drawOutlineText(paint, penTextOutline, penTextFill, rc, QtCore.Qt.AlignRight, u'\xbb')
			#endif
			
			y += self.itemHeight
		#endfor
		paint.end()
	#enddef
	
	
	def closeEvent(self, event):
		"""
		Close event
		"""
		self.htpcController.currentMenuStack.pop()
	#enddef
#endclass


class HTPCController(QtCore.QObject):
	"""
	HTPC Controller
	"""
	
	# event listener for standart input
	eventListener = None
	
	# menu window
	gMenu = None
	
	# cuurrent menu
	currentMenuStack = None
	
	# last ir command
	irCommand = None
	
	
	def __init__(self):
		"""
		Init
		"""
		QtCore.QObject.__init__(self)

		menu.generateMenuItemIds(None, menu.menu['items'])
		
		self.eventListener = HTPCControllerEventListener(self, config.allowStandartInput)
		self.eventListener.show()
		
		self.currentMenuStack = []
		
		if (config.allowStandartInput):
			self.showMainMenu()
		#endif
	#enddef
	
	
	def getCurrentMenu(self):
		"""
		Returns current menu
		"""
		if (self.currentMenuStack):
			return self.currentMenuStack[-1]
		#endif
	#enddef
	
	
	def closeMenu(self):
		"""
		Closes menu
		"""
		pass
	#enddef
	
	
	def clickSelectedMenuItem(self):
		"""
		Clicks selected menu item
		"""
		pass
	#enddef
	
	
	def showMainMenu(self):
		"""
		Shows main menu
		"""
		self.hideMainMenu()
		# show main menu at 100x100
		self.gMenu = HTPCControllerMenu(self, menu.menu, 50, 50)
		self.gMenu.show()
	#enddef
	
	
	def hideMainMenu(self):
		"""
		Hides main menu
		"""
		while (self.getCurrentMenu()):
			self.getCurrentMenu().close()
		#endwhile
		return True
	#enddef
	
	
	def showHideMainMenu(self):
		"""
		Shows/hides main menu
		"""
		if (self.getCurrentMenu()):
			self.hideMainMenu()
		else:
			self.showMainMenu()
		#endif
	#enddef
	
	
	def keyPressEvent(self, event):
		"""
		KeyPress event
		"""
		#print '%0x | %0x' % (event.key(), event.modifiers())
		if (self.getCurrentMenu()):
			if (event.key() == QtCore.Qt.Key_Return):
				self.getCurrentMenu().clickSelectedItem()
			elif (event.key() == QtCore.Qt.Key_Escape):
				self.getCurrentMenu().close()
			elif (event.key() == QtCore.Qt.Key_Up):
				self.getCurrentMenu().selectPreviousItem()
			elif (event.key() == QtCore.Qt.Key_Down):
				self.getCurrentMenu().selectNextItem()
			elif (event.key() == QtCore.Qt.Key_Left):
				self.getCurrentMenu().close()
			elif (event.key() == QtCore.Qt.Key_Right):
				self.getCurrentMenu().clickSelectedItem()
			#endif
		#endif
		
		if (event.key() == QtCore.Qt.Key_M):
			self.showHideMainMenu()
		#endif
	#enddef
	
	
	def setIrCommand(self, command):
		"""
		Sets ir command
		"""
		self.irCommand = command
	#enddef
	
	
	def irEvent(self):
		"""
		IR event
		"""
		command = self.irCommand
		window = control.getFocusedWindow()
		
		processed  = False
		
		try:
			if (config.irEventSoundFilename):
				pygame.mixer.music.play()
			#endif
		except Exception, e:
			dbg.log('IREvent exception: %s', str(e))
		#endtry
		
		try:
			if (control.isHTPCController(window) or self.getCurrentMenu()):
				if (command == "Left"):
					processed  = True
					if (self.getCurrentMenu()):
						self.getCurrentMenu().close()
					#endif
				
				elif (command == "Up"):
					processed  = True
					if (self.getCurrentMenu()):
						self.getCurrentMenu().selectPreviousItem()
					#endif
				
				elif (command == "Right"):
					processed  = True
					if (self.getCurrentMenu()):
						self.getCurrentMenu().clickSelectedItem()
					#endif
				
				elif (command == "Down"):
					processed  = True
					if (self.getCurrentMenu()):
						self.getCurrentMenu().selectNextItem()
					#endif
				
				elif (command == "Enter"):
					processed  = True
					if (self.getCurrentMenu()):
						self.getCurrentMenu().clickSelectedItem()
					#endif
				
				elif (command == "Back"):
					processed  = True
					if (self.getCurrentMenu()):
						self.getCurrentMenu().close()
					#endif
				
				#endif
			
			# for mplayer
			elif (control.isMPlayer(window)):
				if (command == "Play/Pause"):
					processed  = True
					g.keySimulator.sendString("p")
	
				#endif
			
			# for mythtv
			elif (control.isMythTv(window)):
				if (command == "Play/Pause"):
					processed  = True
					g.keySimulator.sendString("p")
	
				#endif
			
			# for others
			else:
				pass
			#endif
	
			if (not processed):
				if (command == "Left"):
					g.keySimulator.sendString("[Left]")
				
				elif (command == "Right"):
					g.keySimulator.sendString("[Right]")
				
				elif (command == "Up"):
					g.keySimulator.sendString("[Up]")
				
				elif (command == "Down"):
					g.keySimulator.sendString("[Down]")
				
				elif (command == "Enter"):
					g.keySimulator.sendString("[Return]")
				
				elif (command == "Back"):
					g.keySimulator.sendString("[Escape]")
				
				elif (command == "Close"):
					g.keySimulator.sendString("[Escape]")
				
				elif (command == "Menu"):
					self.showHideMainMenu()
	
				elif (command == "Rew"):
					g.keySimulator.sendString("[Prior]")
	
				elif (command == "Fwd"):
					g.keySimulator.sendString("[Next]")
	
				elif (command == "Ch+"):
					g.keySimulator.sendString("[Home]")
	
				elif (command == "Ch-"):
					g.keySimulator.sendString("[End]")
	
				elif (command == "Vol+"):
					control.increaseVolume()
	
				elif (command == "Vol-"):
					control.decreaseVolume()
				
				elif (command == "Tv"):
					if (not control.isRunning(config.mythTvProgramName)):
						control.runMythTV()
					#endif
				
				elif (command == "Recall"):
					if (not control.isRunning(config.funOneProgramName)):
						control.runFun1()
					else:
						control.stopFun1()
					#endif
				
				#endif
			#endif
		except Exception, e:
			dbg.log('IREvent exception: %s', str(e))
		#endtry
	#enddef
#endclass


################################################################################
app = QtGui.QApplication(sys.argv)

confFilename = './HTPCController.conf'
if (len(sys.argv) >= 2):
	confFilename = sys.argv[1]
#endif
config.load(confFilename)

dbg.filename = config.logFilename

dbg.log('Start')
try:
	# create key simulator
	dbg.log('Create key simulator')
	g.keySimulator = KeySimulator.KeySimulator()

	# create htpc controller
	dbg.log('Create HTPCController')
	g.htpcController = HTPCController()
	
	if (config.irEventSoundFilename):
		import pygame
		pygame.mixer.init(44100)
		pygame.mixer.music.load(config.irEventSoundFilename)
	#endif
	
	# run ir listener
	dbg.log('Run ir listener')
	irWorker = ir.run()
	dbg.log('Connect ir events')
	app.connect(irWorker, QtCore.SIGNAL('irEvent'), g.htpcController.irEvent)

	# run
	app.exec_()

	dbg.log('End')
except Exception, e:
	dbg.log('Exception: %s', str(e))
	raise
except:
	dbg.log('Unknown exception')
	raise
#endtry